/*
 * SpriteEditorView.java
 */

package spriteeditor;

import java.awt.FlowLayout;
import java.awt.event.KeyEvent;
import java.util.Observable;
import spriteeditor.sprite.Sprite;
import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.TaskMonitor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Observer;
import javax.swing.BoxLayout;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableModel;
import spriteeditor.sprite.Animation;
import spriteeditor.sprite.Frame;

/**
 * The application's main frame.
 */
public class SpriteEditorView extends FrameView implements Observer
{
    public SpriteEditorView(SingleFrameApplication app) {
        super(app);

        initComponents();
        imagePane = new ImagePane();
        viewport.setViewportView(imagePane);
        tabbedPane.setVisible(false);

        // status bar initialization - message timeout, idle icon and busy animation, etc
        ResourceMap resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
        messageTimer = new Timer(messageTimeout, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText("");
            }
        });
        messageTimer.setRepeats(false);
        int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
        for (int i = 0; i < busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
        statusAnimationLabel.setIcon(idleIcon);
        progressBar.setVisible(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if ("started".equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                    }
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(true);
                } else if ("done".equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    progressBar.setVisible(false);
                    progressBar.setValue(0);
                } else if ("message".equals(propertyName)) {
                    String text = (String)(evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? "" : text);
                    messageTimer.restart();
                } else if ("progress".equals(propertyName)) {
                    int value = (Integer)(evt.getNewValue());
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(false);
                    progressBar.setValue(value);
                }
            }
        });
    }

    public static Sprite getSprite() {
        return sprite;
    }

    public static void setSprite(Sprite sprite) {
        SpriteEditorView.sprite = sprite;
    }

    
    @Action
    public void showAboutBox() {
        if (aboutBox == null) {
            JFrame mainFrame = SpriteEditorApp.getApplication().getMainFrame();
            aboutBox = new SpriteEditorAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        SpriteEditorApp.getApplication().show(aboutBox);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainPanel = new javax.swing.JPanel();
        tabbedPane = new javax.swing.JTabbedPane();
        tablMainParams = new javax.swing.JPanel();
        panelParams = new javax.swing.JPanel();
        lblWidth = new javax.swing.JLabel();
        lblHeight = new javax.swing.JLabel();
        textWidth = new javax.swing.JTextField();
        textHeight = new javax.swing.JTextField();
        lblXIndex = new javax.swing.JLabel();
        lblYIndex = new javax.swing.JLabel();
        textXIndex = new javax.swing.JTextField();
        textYIndex = new javax.swing.JTextField();
        lblMargin = new javax.swing.JLabel();
        textMargin = new javax.swing.JTextField();
        lblBorder = new javax.swing.JLabel();
        textBorder = new javax.swing.JTextField();
        btApply = new javax.swing.JButton();
        tabAnimations = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        tableAnims = new javax.swing.JTable();
        jScrollPane2 = new javax.swing.JScrollPane();
        tableAframes = new javax.swing.JTable();
        btNewAnim = new javax.swing.JButton();
        lblAnimFrame = new javax.swing.JLabel();
        btNewAnim1 = new javax.swing.JButton();
        btNewAnim2 = new javax.swing.JButton();
        btNewAnim3 = new javax.swing.JButton();
        btFrameDel = new javax.swing.JButton();
        btFrameUp = new javax.swing.JButton();
        btFrameDn = new javax.swing.JButton();
        viewport = new javax.swing.JScrollPane();
        framesPane = new javax.swing.JScrollPane();
        menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu fileMenu = new javax.swing.JMenu();
        itmNewSprite = new javax.swing.JMenuItem();
        itmLoadSprite = new javax.swing.JMenuItem();
        itemSaveSprite = new javax.swing.JMenuItem();
        javax.swing.JMenuItem exitMenuItem = new javax.swing.JMenuItem();
        javax.swing.JMenu helpMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
        statusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();
        jPanel3 = new javax.swing.JPanel();

        mainPanel.setName("mainPanel"); // NOI18N

        tabbedPane.setName("propPanel"); // NOI18N
        tabbedPane.addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentShown(java.awt.event.ComponentEvent evt) {
                tabbedPaneComponentShown(evt);
            }
        });

        tablMainParams.setName("tablMainParams"); // NOI18N
        tablMainParams.addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentShown(java.awt.event.ComponentEvent evt) {
                tablMainParamsComponentShown(evt);
            }
        });

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(spriteeditor.SpriteEditorApp.class).getContext().getResourceMap(SpriteEditorView.class);
        panelParams.setBorder(javax.swing.BorderFactory.createTitledBorder(resourceMap.getString("panelParams.border.title"))); // NOI18N
        panelParams.setName("panelParams"); // NOI18N

        lblWidth.setText(resourceMap.getString("lblWidth.text")); // NOI18N
        lblWidth.setName("lblWidth"); // NOI18N

        lblHeight.setText(resourceMap.getString("lblHeight.text")); // NOI18N
        lblHeight.setName("lblHeight"); // NOI18N

        textWidth.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        textWidth.setText(resourceMap.getString("textWidth.text")); // NOI18N
        textWidth.setName("textWidth"); // NOI18N

        textHeight.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        textHeight.setText(resourceMap.getString("textHeight.text")); // NOI18N
        textHeight.setName("textHeight"); // NOI18N

        lblXIndex.setText(resourceMap.getString("lblXIndex.text")); // NOI18N
        lblXIndex.setName("lblXIndex"); // NOI18N

        lblYIndex.setText(resourceMap.getString("lblYIndex.text")); // NOI18N
        lblYIndex.setName("lblYIndex"); // NOI18N

        textXIndex.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        textXIndex.setText(resourceMap.getString("textXIndex.text")); // NOI18N
        textXIndex.setName("textXIndex"); // NOI18N

        textYIndex.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        textYIndex.setText(resourceMap.getString("textYIndex.text")); // NOI18N
        textYIndex.setName("textYIndex"); // NOI18N

        lblMargin.setText(resourceMap.getString("lblMargin.text")); // NOI18N
        lblMargin.setName("lblMargin"); // NOI18N

        textMargin.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        textMargin.setText(resourceMap.getString("textMargin.text")); // NOI18N
        textMargin.setName("textMargin"); // NOI18N

        lblBorder.setText(resourceMap.getString("lblBorder.text")); // NOI18N
        lblBorder.setName("lblBorder"); // NOI18N

        textBorder.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        textBorder.setText(resourceMap.getString("textBorder.text")); // NOI18N
        textBorder.setName("textBorder"); // NOI18N

        btApply.setText(resourceMap.getString("btApply.text")); // NOI18N
        btApply.setName("btApply"); // NOI18N
        btApply.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btApplyActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout panelParamsLayout = new javax.swing.GroupLayout(panelParams);
        panelParams.setLayout(panelParamsLayout);
        panelParamsLayout.setHorizontalGroup(
            panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panelParamsLayout.createSequentialGroup()
                .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(panelParamsLayout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(lblMargin)
                            .addComponent(lblBorder)))
                    .addGroup(panelParamsLayout.createSequentialGroup()
                        .addGap(10, 10, 10)
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(lblWidth)
                            .addComponent(lblHeight))))
                .addGap(18, 18, 18)
                .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addComponent(textWidth, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 34, Short.MAX_VALUE)
                    .addComponent(textBorder)
                    .addComponent(textMargin, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 34, Short.MAX_VALUE)
                    .addComponent(textHeight, javax.swing.GroupLayout.Alignment.LEADING))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 46, Short.MAX_VALUE)
                .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(panelParamsLayout.createSequentialGroup()
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(lblXIndex)
                            .addComponent(lblYIndex))
                        .addGap(18, 18, 18)
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                            .addComponent(textYIndex)
                            .addComponent(textXIndex, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE)))
                    .addComponent(btApply))
                .addGap(73, 73, 73))
        );
        panelParamsLayout.setVerticalGroup(
            panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panelParamsLayout.createSequentialGroup()
                .addGap(14, 14, 14)
                .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(panelParamsLayout.createSequentialGroup()
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(lblXIndex)
                            .addComponent(textXIndex, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGap(12, 12, 12)
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(lblYIndex)
                            .addComponent(textYIndex, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                    .addGroup(panelParamsLayout.createSequentialGroup()
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(lblWidth)
                            .addComponent(textWidth, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGap(12, 12, 12)
                        .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(lblHeight)
                            .addComponent(textHeight, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblMargin)
                    .addComponent(textMargin, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(12, 12, 12)
                .addGroup(panelParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblBorder)
                    .addComponent(textBorder, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(btApply))
                .addGap(38, 38, 38))
        );

        javax.swing.GroupLayout tablMainParamsLayout = new javax.swing.GroupLayout(tablMainParams);
        tablMainParams.setLayout(tablMainParamsLayout);
        tablMainParamsLayout.setHorizontalGroup(
            tablMainParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(tablMainParamsLayout.createSequentialGroup()
                .addComponent(panelParams, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );
        tablMainParamsLayout.setVerticalGroup(
            tablMainParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tablMainParamsLayout.createSequentialGroup()
                .addComponent(panelParams, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(398, Short.MAX_VALUE))
        );

        tabbedPane.addTab(resourceMap.getString("tablMainParams.TabConstraints.tabTitle"), tablMainParams); // NOI18N

        tabAnimations.setName("tabAnimations"); // NOI18N
        tabAnimations.addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentShown(java.awt.event.ComponentEvent evt) {
                tabAnimationsComponentShown(evt);
            }
        });

        jScrollPane1.setName("jScrollPane1"); // NOI18N

        tableAnims.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Index", "FramesCount", "Delay"
            }
        ));
        tableAnims.setName("tableAnims"); // NOI18N
        tableAnims.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                tableAnimsMouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(tableAnims);

        jScrollPane2.setName("jScrollPane2"); // NOI18N

        tableAframes.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {

            }
        ));
        tableAframes.setName("tableAframes"); // NOI18N
        tableAframes.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                tableAframesMouseClicked(evt);
            }
        });
        jScrollPane2.setViewportView(tableAframes);

        btNewAnim.setText(resourceMap.getString("btNewAnim.text")); // NOI18N
        btNewAnim.setName("btNewAnim"); // NOI18N
        btNewAnim.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btNewAnimActionPerformed(evt);
            }
        });

        lblAnimFrame.setText(resourceMap.getString("lblAnimFrame.text")); // NOI18N
        lblAnimFrame.setName("lblAnimFrame"); // NOI18N

        btNewAnim1.setText(resourceMap.getString("btNewAnim1.text")); // NOI18N
        btNewAnim1.setName("btNewAnim1"); // NOI18N

        btNewAnim2.setText(resourceMap.getString("btNewAnim2.text")); // NOI18N
        btNewAnim2.setName("btNewAnim2"); // NOI18N

        btNewAnim3.setText(resourceMap.getString("btNewAnim3.text")); // NOI18N
        btNewAnim3.setName("btNewAnim3"); // NOI18N

        btFrameDel.setText(resourceMap.getString("btFrameDel.text")); // NOI18N
        btFrameDel.setName("btFrameDel"); // NOI18N
        btFrameDel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btFrameDelActionPerformed(evt);
            }
        });

        btFrameUp.setText(resourceMap.getString("btFrameUp.text")); // NOI18N
        btFrameUp.setName("btFrameUp"); // NOI18N
        btFrameUp.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btFrameUpActionPerformed(evt);
            }
        });

        btFrameDn.setText(resourceMap.getString("btFrameDn.text")); // NOI18N
        btFrameDn.setName("btFrameDn"); // NOI18N
        btFrameDn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btFrameDnActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout tabAnimationsLayout = new javax.swing.GroupLayout(tabAnimations);
        tabAnimations.setLayout(tabAnimationsLayout);
        tabAnimationsLayout.setHorizontalGroup(
            tabAnimationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(tabAnimationsLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(tabAnimationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 309, Short.MAX_VALUE)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 309, Short.MAX_VALUE)
                    .addGroup(tabAnimationsLayout.createSequentialGroup()
                        .addComponent(btNewAnim)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btNewAnim1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 93, Short.MAX_VALUE)
                        .addComponent(btNewAnim2)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btNewAnim3))
                    .addComponent(lblAnimFrame)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tabAnimationsLayout.createSequentialGroup()
                        .addComponent(btFrameDel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 152, Short.MAX_VALUE)
                        .addComponent(btFrameUp)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btFrameDn)))
                .addContainerGap())
        );
        tabAnimationsLayout.setVerticalGroup(
            tabAnimationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(tabAnimationsLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 235, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(tabAnimationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(btNewAnim)
                    .addComponent(btNewAnim1)
                    .addComponent(btNewAnim2)
                    .addComponent(btNewAnim3))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(lblAnimFrame)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 244, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(tabAnimationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(btFrameUp)
                    .addComponent(btFrameDn)
                    .addComponent(btFrameDel))
                .addContainerGap())
        );

        tabbedPane.addTab(resourceMap.getString("tabAnimations.TabConstraints.tabTitle"), tabAnimations); // NOI18N

        viewport.setName("viewport"); // NOI18N
        viewport.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
            public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
                viewportMouseWheelMoved(evt);
            }
        });
        viewport.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                viewportMousePressed(evt);
            }
        });
        viewport.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseDragged(java.awt.event.MouseEvent evt) {
                viewportMouseDragged(evt);
            }
        });

        framesPane.setName("framesPane"); // NOI18N

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(viewport, javax.swing.GroupLayout.DEFAULT_SIZE, 722, Short.MAX_VALUE)
                    .addComponent(framesPane, javax.swing.GroupLayout.DEFAULT_SIZE, 722, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(tabbedPane, javax.swing.GroupLayout.PREFERRED_SIZE, 334, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(tabbedPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 623, Short.MAX_VALUE)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
                .addComponent(viewport, javax.swing.GroupLayout.DEFAULT_SIZE, 415, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(framesPane, javax.swing.GroupLayout.PREFERRED_SIZE, 202, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        menuBar.setName("menuBar"); // NOI18N

        fileMenu.setText(resourceMap.getString("fileMenu.text")); // NOI18N
        fileMenu.setName("fileMenu"); // NOI18N
        fileMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                fileMenuActionPerformed(evt);
            }
        });

        itmNewSprite.setText(resourceMap.getString("itmNewSprite.text")); // NOI18N
        itmNewSprite.setName("itmNewSprite"); // NOI18N
        itmNewSprite.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itmNewSpriteActionPerformed(evt);
            }
        });
        fileMenu.add(itmNewSprite);

        itmLoadSprite.setText(resourceMap.getString("itmLoadSprite.text")); // NOI18N
        itmLoadSprite.setName("itmLoadSprite"); // NOI18N
        itmLoadSprite.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itmLoadSpriteActionPerformed(evt);
            }
        });
        fileMenu.add(itmLoadSprite);

        itemSaveSprite.setText(resourceMap.getString("itemSaveSprite.text")); // NOI18N
        itemSaveSprite.setName("itemSaveSprite"); // NOI18N
        itemSaveSprite.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemSaveSpriteActionPerformed(evt);
            }
        });
        fileMenu.add(itemSaveSprite);

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(spriteeditor.SpriteEditorApp.class).getContext().getActionMap(SpriteEditorView.class, this);
        exitMenuItem.setAction(actionMap.get("quit")); // NOI18N
        exitMenuItem.setName("exitMenuItem"); // NOI18N
        fileMenu.add(exitMenuItem);

        menuBar.add(fileMenu);

        helpMenu.setText(resourceMap.getString("helpMenu.text")); // NOI18N
        helpMenu.setName("helpMenu"); // NOI18N

        aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        statusPanel.setName("statusPanel"); // NOI18N

        statusPanelSeparator.setName("statusPanelSeparator"); // NOI18N

        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N

        progressBar.setName("progressBar"); // NOI18N

        javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(statusPanelSeparator, javax.swing.GroupLayout.DEFAULT_SIZE, 1062, Short.MAX_VALUE)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusMessageLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 892, Short.MAX_VALUE)
                .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusAnimationLabel)
                .addContainerGap())
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addComponent(statusPanelSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(statusMessageLabel)
                    .addComponent(statusAnimationLabel)
                    .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(3, 3, 3))
        );

        jPanel3.setName("jPanel3"); // NOI18N

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 100, Short.MAX_VALUE)
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 100, Short.MAX_VALUE)
        );

        setComponent(mainPanel);
        setMenuBar(menuBar);
        setStatusBar(statusPanel);
    }// </editor-fold>//GEN-END:initComponents

    private void fileMenuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileMenuActionPerformed
        // TODO add your handling code here:

    }//GEN-LAST:event_fileMenuActionPerformed

    private void itmNewSpriteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itmNewSpriteActionPerformed
        // TODO add your handling code here:
        sprite = new Sprite();
        sprite.addObserver(this);
        JFileChooser fileChooser = new JFileChooser();
        int result = fileChooser.showOpenDialog(getFrame());
        if (result == JFileChooser.APPROVE_OPTION)
        {
            File file = fileChooser.getSelectedFile();
            sprite.loadSpriteImage(file);
            refreshParamsPanel();
            tabbedPane.setVisible(true);
            imagePane.centerCam();
        }
    }//GEN-LAST:event_itmNewSpriteActionPerformed
    int prevX = 0, prevY = 0;
    private void viewportMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_viewportMouseDragged
        if ((evt.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0)
        {
            imagePane.moveCam(evt.getX() - prevX, evt.getY() - prevY);
            prevX = evt.getX();
            prevY = evt.getY();
        }        // TODO add your handling code here:
    }//GEN-LAST:event_viewportMouseDragged

    private void viewportMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_viewportMousePressed
         if ((evt.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0)
        {
            prevX = evt.getX();
            prevY = evt.getY();
        }
    }//GEN-LAST:event_viewportMousePressed

    private void viewportMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_viewportMouseWheelMoved
        // TODO add your handling code here:
          imagePane.changeZoom(-evt.getWheelRotation() * 0.1);
    }//GEN-LAST:event_viewportMouseWheelMoved

    private void itemSaveSpriteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemSaveSpriteActionPerformed
        // TODO add your handling code here:
        if(sprite != null)
            sprite.saveSprite();
    }//GEN-LAST:event_itemSaveSpriteActionPerformed

    private void itmLoadSpriteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itmLoadSpriteActionPerformed
        // TODO add your handling code here:
        JFileChooser fileChooser = new JFileChooser();
        System.out.println(System.getProperty("user.dir"));
        fileChooser.setSelectedFile(new File(System.getProperty("user.dir") +"\\"));
        fileChooser.setFileFilter(new FileFilter() {

            @Override
            public boolean accept(File f) {
               return  f.isDirectory() || f.getName().endsWith(".spr");
            }

            @Override
            public String getDescription() {
                return "*.spr files";
            }
        });
        int result = fileChooser.showOpenDialog(getFrame());
        if (result == JFileChooser.APPROVE_OPTION)
        {
            File file = fileChooser.getSelectedFile();
            sprite = Sprite.LoadSprite(file);
            sprite.addObserver(this);
            tabbedPane.setVisible(true);
            refreshParamsPanel();
            refreshFrames();
            refreshAnimsPanel();
            imagePane.centerCam();
        }
    }//GEN-LAST:event_itmLoadSpriteActionPerformed

    private void btNewAnimActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btNewAnimActionPerformed
        // TODO add your handling code here:
        Animation a = new Animation();
        sprite.addAnim(a);
    }//GEN-LAST:event_btNewAnimActionPerformed

    private void tabbedPaneComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_tabbedPaneComponentShown
    }//GEN-LAST:event_tabbedPaneComponentShown

    private void tablMainParamsComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_tablMainParamsComponentShown
        if(sprite == null)
            return;
        sprite.setCurAnim(null);
        sprite.setCurFrame(null);
    }//GEN-LAST:event_tablMainParamsComponentShown

    private void tabAnimationsComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_tabAnimationsComponentShown
        // TODO add your handling code here:
        refreshAnimsPanel();
    }//GEN-LAST:event_tabAnimationsComponentShown

    private void tableAframesMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tableAframesMouseClicked
        // TODO add your handling code here:
         int row = tableAframes.getSelectedRow();
        if (row == -1)
            return;
        Frame f = sprite.getCurAnim().getFrame(row);
        sprite.setCurFrame(f);
    }//GEN-LAST:event_tableAframesMouseClicked

    private void btFrameUpActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btFrameUpActionPerformed
        // TODO add your handling code here:
        sprite.currAnimMoveFrame(tableAframes.getSelectedRow(), -1);
    }//GEN-LAST:event_btFrameUpActionPerformed

    private void btFrameDnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btFrameDnActionPerformed
        // TODO add your handling code here:
        sprite.currAnimMoveFrame(tableAframes.getSelectedRow(), 1);
    }//GEN-LAST:event_btFrameDnActionPerformed

    private void tableAnimsMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tableAnimsMouseClicked
        // TODO add your handling code here:
        int row = tableAnims.getSelectedRow();
        if(row != -1)
            sprite.setCurAnim(row);
    }//GEN-LAST:event_tableAnimsMouseClicked

    private void btFrameDelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btFrameDelActionPerformed
        // TODO add your handling code here:
        sprite.currAnimRemoveFrame(tableAframes.getSelectedRow());
        sprite.setCurFrame(null);
    }//GEN-LAST:event_btFrameDelActionPerformed

    private void btApplyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btApplyActionPerformed
        // TODO add your handling code here:
        sprite.setFrameW(getVal(textWidth));
        sprite.setFrameH(getVal(textHeight));
        sprite.setMargin(getVal(textMargin));
        sprite.setBorder(getVal(textBorder));
        sprite.setxIndex(getVal(textXIndex));
        sprite.setyIndex(getVal(textYIndex));
        sprite.requestFramesRefresh();
    }//GEN-LAST:event_btApplyActionPerformed

    private void refreshParamsPanel ()
    {
        textMargin.setText(String.valueOf(sprite.getMargin()));
        textBorder.setText(String.valueOf(sprite.getBorder()));
        textWidth.setText(String.valueOf(sprite.getFrameW()));
        textHeight.setText(String.valueOf(sprite.getFrameH()));
        textXIndex.setText(String.valueOf(sprite.getxIndex()));
        textYIndex.setText(String.valueOf(sprite.getyIndex()));
    }

    private void refreshFrames()
    {
       ArrayList <Frame> frames = sprite.getFrames();
       JPanel jp = new JPanel();
       jp.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 0));
       for(Frame f : frames)
       {
             jp.add(f);
       }
       framesPane.setViewportView(jp);
    }
    
    private int getVal(JTextField field)
    {
        String s = field.getText();
        if (s.isEmpty())
            return 0;
        return  Integer.parseInt(s);
    }

    private void refreshAnimsPanel()
    {
        if(sprite == null)
            return;
        DefaultTableModel tm = new DefaultTableModel();
        String[][] data = new String[sprite.getAnimsCount()][3];
        ArrayList <Animation> anims = sprite.getAnims();
        for(int i = 0; i < sprite.getAnimsCount(); i++)
        {
            Animation a = anims.get(i);
            data[i][0] = String.valueOf(i);
            data[i][1] = String.valueOf(a.getFramesCount());
            data[i][2] = String.valueOf(a.getDelay());
        }
        tm.setDataVector(data, new String[]{"Index", "FramesCount", "Delay"});
        tableAnims.setModel(tm);
        Animation anim = sprite.getCurAnim();
        int selectedRow = anims.indexOf(anim);
        tableAnims.getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
        selectedRow = -1;
        tm = new DefaultTableModel();
        if(anim != null)
        {
            ArrayList <Frame> frames = anim.getFrames();
            data = new String[frames.size()][4];
            for(int i = 0; i < frames.size(); i++)
            {
                Frame f = frames.get(i);
                data[i][0] = String.valueOf(i);
                data[i][1] = String.valueOf(f.getId());
                data[i][2] = String.valueOf(f.getXIndex());
                data[i][3] = String.valueOf(f.getYindex());
            }
            tm.setDataVector(data, new String[]{"Index", "id", "x", "y"});
            if(sprite.getCurFrame() != null)
            {
                selectedRow = frames.indexOf(sprite.getCurFrame());
            }
        }
        tableAframes.setModel(tm);
        if(selectedRow != -1)
            tableAframes.getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
        
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btApply;
    private javax.swing.JButton btFrameDel;
    private javax.swing.JButton btFrameDn;
    private javax.swing.JButton btFrameUp;
    private javax.swing.JButton btNewAnim;
    private javax.swing.JButton btNewAnim1;
    private javax.swing.JButton btNewAnim2;
    private javax.swing.JButton btNewAnim3;
    private javax.swing.JScrollPane framesPane;
    private javax.swing.JMenuItem itemSaveSprite;
    private javax.swing.JMenuItem itmLoadSprite;
    private javax.swing.JMenuItem itmNewSprite;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JLabel lblAnimFrame;
    private javax.swing.JLabel lblBorder;
    private javax.swing.JLabel lblHeight;
    private javax.swing.JLabel lblMargin;
    private javax.swing.JLabel lblWidth;
    private javax.swing.JLabel lblXIndex;
    private javax.swing.JLabel lblYIndex;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JPanel panelParams;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    private javax.swing.JPanel tabAnimations;
    private javax.swing.JTabbedPane tabbedPane;
    private javax.swing.JPanel tablMainParams;
    private javax.swing.JTable tableAframes;
    private javax.swing.JTable tableAnims;
    private javax.swing.JTextField textBorder;
    private javax.swing.JTextField textHeight;
    private javax.swing.JTextField textMargin;
    private javax.swing.JTextField textWidth;
    private javax.swing.JTextField textXIndex;
    private javax.swing.JTextField textYIndex;
    private javax.swing.JScrollPane viewport;
    // End of variables declaration//GEN-END:variables

    private final Timer messageTimer;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;

    private JDialog aboutBox;
    static Sprite sprite = null;
    private ImagePane imagePane;

    public void update(Observable o, Object arg)
    {
        if(Sprite.REFRESH_ANIMS_PANEL.equals(arg))
        {
            refreshAnimsPanel();
        }
        else if(Sprite.REFRESH_FRAMES.equals(arg))
        {
            sprite.refreshFramesAndAnims();
            refreshFrames();
        }
    }
}
